/* Copyright (C) 2015-2016 HangZhou Zenzet Technology Co., Ltd.
 * All right reserved

 * File:crypto/locketaes.c
 * Author:guojianchuan/max
 * Date:2016-06-02

 */

/* system header */
#include <string.h>

/* 3rd project header */
#include "openssl/evp.h"

/* my project header */
#include "locketdef.h"
#include "locketerrno.h"
#include "locketlog.h"
#include "locketcommon.h"

#include "locketconvert.h"

/* 不带0x前缀的一个hex串长度, 比如"ff" */
#define LOCKET_HEX_NOPREFIX_MAX_LEN  2

/* 带0x前缀的一个hex串长度, 比如"0xff" */
#define LOCKET_HEX_PREFIX_MAX_LEN  (LOCKET_HEX_NOPREFIX_MAX_LEN * 2)

/**
* @Function:LOCKET_CIPHER_CheckNewLine
* @Author: guojianchuan/max
* @Date: 2016-09-08
* @Description: Check base64 string contain newline character
* @caution: 
*/
static int LOCKET_CIPHER_CheckNewLine (unsigned char *pucInput, int iLen)
{
    int iNewLine = 0;
    int i = 0;

    for (i = 0; i < iLen; i++)
    {
        if (('\r' == pucInput[i]) || ('\n' == pucInput[i]))
        {
            iNewLine = 1;
            break;
        }
    }

    return iNewLine;
}

/**
* @Function:LOCKET_CIPHER_DeleteNewLine
* @Author: guojianchuan/max
* @Date: 2016-09-08
* @Description: Delete newline character
* @caution: 
*/
static void LOCKET_CIPHER_DeleteNewLine (unsigned char *pucInput, int iLen, unsigned char *pucOutput, int *piOutLen)
{
    int i = 0;
    int j = 0;

    for (i = 0; i < iLen; i++)
    {
        if (('\r' != pucInput[i]) && ('\n' != pucInput[i]))
        {
            pucOutput[j++] = pucInput[i];
        }
    }

    *piOutLen = j;

    return;
}

/**
* @Function:LOCKET_CIPHER_ByteToHex
* @Author: guojianchuan/max
* @Date: 2016-06-03
* @Description: Convert byte to hex
* @caution: 
*/
static unsigned char* LOCKET_CIPHER_ByteToHex (IN unsigned char *pucInput, IN int iInputLen, OUT unsigned char *pucOutput, OUT int *piOutputLen)
{
    if ((NULL == pucInput) || (NULL == pucOutput) || (NULL == piOutputLen))
    {
        return NULL;
    }

    if (0 == iInputLen)
    {
        return NULL;
    }

    int i = 0;

    for (i = 0; i < iInputLen; i++)
    {
        /* muse be add 1 with LOCKET_HEX_NOPREFIX_MAX_LEN */
        snprintf ((char*) (pucOutput + 2 * i), LOCKET_HEX_NOPREFIX_MAX_LEN + 1, "%.02x", pucInput[i]);
    }

    *piOutputLen = 2 * i;

    return pucOutput;
}

/**
* @Function:LOCKET_CIPHER_HexToByte
* @Author: guojianchuan/max
* @Date: 2016-06-03
* @Description: Convert hex to byte
* @caution: 
*/
static unsigned char* LOCKET_CIPHER_HexToByte (IN unsigned char *pucInput, IN int iInputLen, OUT unsigned char *pucOutput, OUT int *piOutputLen)
{
    if ((NULL == pucInput) || (NULL == pucOutput) || (NULL == piOutputLen))
    {
        return NULL;
    }

    if ((0 != iInputLen) && (0 != iInputLen % 2))
    {
        LOCKET_LOGERROR ("input len is invalid");
        return NULL;
    }

    int i         = 0;
    int j         = 0;
    unsigned char ucNum     = 0;
    unsigned char szHex[LOCKET_HEX_PREFIX_MAX_LEN + 1]  = {0};

    for (i = 0; i < iInputLen; i = i+2)
    {
        snprintf ((char*) szHex, sizeof (szHex), "0x%c%c", pucInput[i], pucInput[i+1]);
        sscanf ((char*) szHex, "%x", (int*) &pucOutput[j++]);
    }

    *piOutputLen = j;

    return pucOutput;
}

/**
* @Function:LOCKET_CIPHER_ByteToBase64 
* @Author: guojianchuan/max
* @Date: 2016-06-03
* @Description: Base64 Encode
* @caution: 
*/
static unsigned char* LOCKET_CIPHER_ByteToBase64 (IN unsigned char *pucInput, 
                                                  IN int iInputLen, 
                                                  INOUT unsigned char *pucOutput, 
                                                  OUT int *piOutputLen)
{
    int iErr = ERR_COMMON_SUCCESS;

    /* Check param */
    if ((NULL == pucInput) || (NULL == pucOutput) || (NULL == piOutputLen))
    {
        return NULL;
    }
    if (0 == iInputLen)
    {
        return NULL;
    }

    /* Convert byte to base64 (输出结尾不输出换行符)*/
    int iLen = 0;
    iLen = EVP_EncodeBlock(pucOutput, pucInput, iInputLen);

    *piOutputLen = iLen;

    return pucOutput;
}

/**
* @Function:LOCKET_CIPHER_Base64ToByte
* @Author: guojianchuan/max
* @Date: 2016-06-03
* @Description: Base64 Decode
* @caution: 
*/
static unsigned char* LOCKET_CIPHER_Base64ToByte (IN unsigned char *pucInput, 
                                                  IN int iInputLen, 
                                                  INOUT unsigned char *pucOutput, 
                                                  OUT int *piOutputLen)
{
    int iErr = ERR_COMMON_SUCCESS;

    /* Check param */
    if ((NULL == pucInput) || (NULL == pucOutput) || (NULL == piOutputLen))
    {
        return NULL;
    }
    if (0 == iInputLen)
    {
        return NULL;
    }

    /* 首先去除base64串中的换行符号 */
    unsigned char* pucAfterDel = NULL;
    int iLenAfterDel = 0;
    int iNewLine = 0;
    if (LOCKET_CIPHER_CheckNewLine (pucInput, iInputLen))
    {
        iNewLine = 1;
        pucAfterDel = (unsigned char*) LOCKET_malloc (iInputLen);
        if (NULL == pucAfterDel)
        {
            return NULL;
        }
        LOCKET_CIPHER_DeleteNewLine (pucInput, iInputLen, pucAfterDel, &iLenAfterDel);
    }
    else
    {
        pucAfterDel  = pucInput;
        iLenAfterDel = iInputLen;
    }

    /* 计算输入的base64结尾,有多少个'=' */
    int i             = 0;
    int iEqualCharNum = 0;
    int iInputLenTmp  = iLenAfterDel;
    while ('=' == pucAfterDel[--iInputLenTmp])
    {
        iEqualCharNum++;
    }

    /* Convert base64 to byte (输入不接受换行符)*/
    int iLen = 0;
    iLen = EVP_DecodeBlock(pucOutput, pucAfterDel, iLenAfterDel);

    if (iNewLine)
    {
        LOCKET_free ((void**)&pucAfterDel);
    }

    if (-1 == iLen)
    {
        return NULL;
    }

    /* 
     * 减去等号的数量(iLen的长度必须能够和3整除,所以iLen包含的长度有可能是补零的长度) 
     * 输入中有几个'='号,那么输出也要减去几个'='号
     * 对于输入是字符串来说,影响不大;如果输入是随机数据,会导致数据解码不正确
     */
    iLen = iLen - iEqualCharNum;
    
    *piOutputLen = iLen;

    return pucOutput;
}

/**
* @Function:LOCKET_CIPHER_ByteToOther
* @Author: guojianchuan/max
* @Date: 2016-06-02
* @Description: Convert Hex/Base64 to Byte
* @caution: 
*/
unsigned char *LOCKET_CIPHER_ByteToOther (IN int ulMode, 
                                          IN unsigned char *pucInput, 
                                          IN int iInputLen, 
                                          IN unsigned char *pucOutput, 
                                          OUT int *piOutputLen)
{
    if ((NULL == pucInput) || (NULL == pucOutput) || (NULL == piOutputLen))
    {
        return NULL;
    }
    if (0 == iInputLen)
    {
        return NULL;
    }

    unsigned char *pucConvert = NULL;

    if (ENCODE_HEX == ulMode)
    {
        /* Convert byte to hex */
        pucConvert = LOCKET_CIPHER_ByteToHex (pucInput, iInputLen, pucOutput, piOutputLen);
    }
    else if (ENCODE_BASE64 == ulMode)
    {
        /* Convert byte to base64 */
        pucConvert = LOCKET_CIPHER_ByteToBase64(pucInput, iInputLen, pucOutput, piOutputLen);
    }
    else if (ENCODE_BINARY == ulMode)
    {
        /* no convert */
        pucConvert = pucInput;
    }
    else 
    {
        /* wrong encode format */
        pucConvert = NULL;
    }

    return pucConvert;
}

/**
* @Function:LOCKET_CIPHER_OtherToByte
* @Author: guojianchuan/max
* @Date: 2016-06-02
* @Description: Convert Byte to Hex/Base64
* @caution: 
*/
unsigned char *LOCKET_CIPHER_OtherToByte (IN int ulMode, 
                                          IN unsigned char *pucInput, 
                                          IN int iInputLen, 
                                          IN unsigned char *pucOutput, 
                                          OUT int *piOutputLen)
{
    if ((NULL == pucInput) || (NULL == pucOutput) || (NULL == piOutputLen))
    {
        return NULL;
    }
    if (0 == iInputLen)
    {
        return NULL;
    }

    unsigned char *pucConvert = NULL;

    if (ENCODE_HEX == ulMode)
    {
        /* Convert hex to byte */
        pucConvert = LOCKET_CIPHER_HexToByte (pucInput, iInputLen, pucOutput, piOutputLen);
    }
    else if (ENCODE_BASE64 == ulMode)
    {
        /* Convert base64 to byte */
        pucConvert = LOCKET_CIPHER_Base64ToByte (pucInput, iInputLen, pucOutput, piOutputLen);
    }
    else if (ENCODE_BINARY == ulMode)
    {
        /* no convert */
        pucConvert = pucInput;
    }
    else 
    {
        /* wrong encode format */
        pucConvert = NULL;
    }

    return pucConvert;
}

/**
* @Function:LOCKET_CIPHER_CheckEncode
* @Author: guojianchuan/max
* @Date: 2016-06-20
* @Description: Check encode type
* @caution: 
*/
int LOCKET_CIPHER_CheckEncode (IN int iEncodeType)
{
    if ((iEncodeType < ENCODE_HEX) || (iEncodeType > ENCODE_BINARY))
    {
        return ERR_COMMON_FAILED;
    }
    
    return ERR_COMMON_SUCCESS;
}
